http2: emit session close before stream close#63414
Conversation
|
Review requested:
|
PR-URL: nodejs#63414 Signed-off-by: Matteo Collina <hello@matteocollina.com>
94d6b49 to
0984bca
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #63414 +/- ##
==========================================
+ Coverage 90.05% 90.30% +0.25%
==========================================
Files 714 730 +16
Lines 225742 234164 +8422
Branches 42727 43913 +1186
==========================================
+ Hits 203285 211471 +8186
- Misses 14234 14418 +184
- Partials 8223 8275 +52
🚀 New features to boost your workflow:
|
|
This changes the behaviour exclusively for this one case (where the underlying socket has actually been destroyed). I think there's quite a few other cases where you'll get very similar symptoms (e.g. GOAWAY, any other non-socket session issues that trigger session teardown) where you'll still hit the same event order, and so Undici would still hit the same issue even with this fix. More generally, having the event ordering branch dependingh on error scenarios seems like it's going to be confusing - it'd be better to have a single ordering all the time. And the ordering is a bit surprising, I would normally expect streams within to close before sessions containing them... I worry that flipping the session/stream closure order will just expose some other race condition in the other direction somehow, since this change would now mean that session close would fire while every stream appeared to be open as normal. Can we fix this in a cleaner way? What if we continue closing the streams first, like today (more intuitive imo, keeps single consistent ordering of events) but we emit all errors like this (any problems other than param validation) async instead of sync? Seems safe-ish, since async failure is clearly already possible and has to be handled anyway. There's similar other cases too already: if you call That'd solve Undici's issue: you could still just listen to the events, and nothing will fail synchronously if you retry a new stream when one closes - it's just that the 2nd request will later emit 'error' (as it can in other cases today anyway). That would also match HTTP/1 better. The code there explicitly pushes all initial sync connection issues to next tick: Lines 420 to 441 in 2ebf533 |
PR-URL: nodejs#63414 Signed-off-by: Matteo Collina <hello@matteocollina.com>
|
Thanks, I changed direction here based on the review. Instead of reordering session/stream teardown, this now keeps the existing shutdown order and fixes the race by making So if user code retries from a stream Updated in this branch:
Validated locally with:
|
PR-URL: nodejs#63414 Signed-off-by: Matteo Collina <hello@matteocollina.com>
e2fd7c3 to
4ad1d1f
Compare
|
Follow-up for the Darwin CI failures: I pushed a small test-only adjustment. The implementation change looks fine, but The test now checks the behavior we actually care about:
It now accepts either |
Fixes: #63412
This changes the HTTP/2 session shutdown path so that when the transport has
already been closed, the client session
'close'event is queued before streamclose callbacks are scheduled.
That gives clients a race-free chance to invalidate cached
ClientHttp2Sessioninstances before stream'close'handlers try to reusethat session and synchronously hit
ERR_HTTP2_INVALID_SESSION.